package io.renren.modules.health.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import io.renren.common.constant.Constant;
import io.renren.common.exception.RenException;
import io.renren.common.page.PageData;
import io.renren.common.service.impl.CrudServiceImpl;
import io.renren.modules.health.dao.SportPlanDao;
import io.renren.modules.health.dto.*;
import io.renren.modules.health.entity.FoodPlanEntity;
import io.renren.modules.health.entity.HmCustomerEntity;
import io.renren.modules.health.entity.SportPlanDetailEntity;
import io.renren.modules.health.entity.SportPlanEntity;
import io.renren.modules.health.service.HmCustomerService;
import io.renren.modules.health.service.HmWeightRecordService;
import io.renren.modules.health.service.SportPlanService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.LongStream;

/**
 * 饮食方案
 *
 * @author Mark 
 * @since 1.0.0 2024-03-28
 */
@Service
public class SportPlanServiceImpl extends CrudServiceImpl<SportPlanDao, SportPlanEntity, SportPlanDTO> implements SportPlanService {

    @Autowired
    private SportPlanDetailServiceImpl sportPlanDetailService;
    @Autowired
    private SportServiceImpl sportService;
    @Autowired
    private HmWeightRecordService hmWeightRecordService;
    @Autowired
    private HmCustomerService hmCustomerService;
    @Autowired
    private SportPlanDao sportPlanDao;

    @Override
    public QueryWrapper<SportPlanEntity> getWrapper(Map<String, Object> params){
        String id = (String)params.get("id");
        String cusId = (String)params.get("cusId");
        Boolean currentDate = (Boolean)params.get("currentDate");

        QueryWrapper<SportPlanEntity> wrapper = new QueryWrapper<>();
        wrapper.eq(StrUtil.isNotBlank(id), "id", id);
        wrapper.eq(StrUtil.isNotBlank(cusId), "custom_id", cusId);
        wrapper.ge(currentDate != null && currentDate, "plan_date", LocalDate.now());

        return wrapper;
    }


    @Override
    public void genCustomerSportPlan(long cusId) {
        // 校验是否有今日计划，没有生成
        if (validCurrDayHaveSportPlan(cusId)) return;

        List<SportPlanDTO> sportPlanDTOList = new ArrayList<>(10);
        try {
            // 查询客户信息
            HmCustomerEntity customerEntity = hmCustomerService.selectById(cusId);
            double bmi = calculateBmi(cusId, customerEntity);
            genSportPlan(cusId, bmi, sportPlanDTOList);
            this.insertSportPlanBatch(sportPlanDTOList);
        } catch (RenException e) {
            log.error("运动计划推荐RenException异常",e);
            throw e;
        } catch (Exception e) {
            log.error("运动计划推荐异常",e);
            throw new RenException(10038,"运动计划推荐异常");
        }
    }

    private void insertSportPlanBatch(List<SportPlanDTO> sportPlanDTOList) {
        List<SportPlanDetailEntity> sportPlanDetailSaveList  = new ArrayList<>();
        sportPlanDTOList.forEach(sportPlanDTO -> {
            sportPlanDTO.setId(null);
            sportPlanDTO.setCreator(null);
            sportPlanDTO.setCreateDate(new Date());
            sportPlanDTO.setUpdater(null);
            sportPlanDTO.setUpdateDate(new Date());
            this.save(sportPlanDTO);
            List<SportPlanDetailEntity> sportPlanDetailEntities = sportPlanDTO.getSportPlanDetails().stream().map(sportPlanDetailDTO -> {
                SportPlanDetailEntity foodPlanDetailEntity = new SportPlanDetailEntity();
                BeanUtils.copyProperties(sportPlanDetailDTO,foodPlanDetailEntity);
                foodPlanDetailEntity.setId(null);
                foodPlanDetailEntity.setSportPlanId(sportPlanDTO.getId());
                return foodPlanDetailEntity;
            }).collect(Collectors.toList());
            sportPlanDetailSaveList.addAll(sportPlanDetailEntities);
        });
        sportPlanDetailService.insertBatch(sportPlanDetailSaveList);
    }


    private void genSportPlan(Long cusId, double bmi, List<SportPlanDTO> sportPlanDTOList) {
        Map<String,Object> paramsMap = new HashMap<>(16);
        paramsMap.put("bmi", bmi);
        List<SportDTO> sportDTOS = sportService.list(paramsMap);
        if (sportDTOS == null || sportDTOS.isEmpty())
            throw new RenException(10037,"未查询到符合条件运动");

        paramsMap = new HashMap<>();
        paramsMap.put("cusId",cusId);
        paramsMap.put("orderFiled","create_date");

        // 第一层日期,生成7天
        LongStream.range(0,7).forEach(day -> {
            SportPlanDTO sportPlanDTO = new SportPlanDTO();
            sportPlanDTO.setCustomId(cusId);
            sportPlanDTO.setStatus(1);
            sportPlanDTO.setPlanDate(Date.from(LocalDate.now().plusDays(day).atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));

            // 早、晚一个饮品一个主食，中一个主食，不需要饮品
            for (int i = 0; i < 3; i++) {
                createSportAddToPlan(sportDTOS, sportPlanDTO);
            }
            sportPlanDTOList.add(sportPlanDTO);
        });
    }

    private void createSportAddToPlan(List<SportDTO> sportDTOS, SportPlanDTO sportPlanDTO) {
        Random random = new Random();
        SportDTO sportDTO;
        int i = 0;
        while (true) {
            if (++i > 100) return;
            sportDTO = sportDTOS.get(random.nextInt(sportDTOS.size()));
            SportDTO finalFoodDTO = sportDTO;
            if (sportPlanDTO.getSportPlanDetails().stream().anyMatch(sportDTO1 -> Objects.equals(finalFoodDTO.getId(), sportDTO1.getSportId()))){
                continue;
            }
            break;
        }

        SportPlanDetailDTO foodPlanDetailDTO = new SportPlanDetailDTO();
        foodPlanDetailDTO.setSportId(sportDTO.getId())
                .setSportName(sportDTO.getSportName())
                .setDuration(sportDTO.getDuration())
                .setTrophy(sportDTO.getSportTrophy())
                .setDeleted(0);
        sportPlanDTO.getSportPlanDetails().add(foodPlanDetailDTO);
    }

    @Override
    public List<SportPlanDTO> querySportPlanWithDetail(long cusId) {
        HashMap<String, Object> param = new HashMap<>();
        param.put("cusId",String.valueOf(cusId));
        param.put("currentDate",true);
        List<SportPlanDTO> sportPlanDTOS = this.list(param);
        param = new HashMap<>();
        param.put("sportPlanIds",sportPlanDTOS.stream().map(SportPlanDTO::getId).collect(Collectors.toList()));
        List<SportPlanDetailDTO> sportPlanDetailDTOS = sportPlanDetailService.list(param);
        param = new HashMap<>();
        param.put("sportIds",sportPlanDetailDTOS.stream().map(SportPlanDetailDTO::getSportId).collect(Collectors.toList()));
        List<SportDTO> sportDTOS = sportService.list(param);
        sportPlanDetailDTOS.forEach(sportPlanDetailDTO ->  {
            sportDTOS.forEach(sportDTO -> {
                if (sportPlanDetailDTO.getSportId().equals(sportDTO.getId())) {
                    sportPlanDetailDTO.setSportName(sportDTO.getSportName())
                            .setSportIconRrl(sportDTO.getSportIconUrl());
                }
            });
        });
        sportPlanDTOS.forEach(sportPlanDTO ->  {
            sportPlanDTO.setSportPlanDetails(sportPlanDetailDTOS
                    .stream()
                    .filter(detail -> detail.getSportPlanId().equals(sportPlanDTO.getId()))
                    .collect(Collectors.toList()));
        });
        return sportPlanDTOS;
    }

    /**
     * 校验是否有今日计划，没有生成
     * @param cusId
     * @return
     */
    private boolean validCurrDayHaveSportPlan(Long cusId) {
        Long planCount = sportPlanDao.selectCount(new QueryWrapper<SportPlanEntity>()
                .eq("custom_id", cusId)
                .ge("plan_date",LocalDate.now())
        );
        return planCount > 0;
    }

    /**
     * 获取BMI值
     *
     * @param cusId 客户ID
     * @param customerEntity 客户实体对象
     * @return 返回BMI值
     */
    private  double calculateBmi(Long cusId, HmCustomerEntity customerEntity) {
        // 查询最新体重
        HashMap<String, Object> params = new HashMap<>();
        params.put(Constant.PAGE,"1");
        params.put(Constant.LIMIT,"1");
        params.put("cusId", cusId);
        params.put(Constant.ORDER,Constant.DESC);
        params.put(Constant.ORDER_FIELD,"update_date");
        PageData<HmWeightRecordDTO> page = hmWeightRecordService.page(params);
        return page.getList().get(0).getWeight() / (Double.parseDouble(String.valueOf(customerEntity.getHeight())) / 100);
    }
}